package com.sfac.springboot.modules.common.controller;

import com.sfac.springboot.modules.common.entity.ExceptionLog;
import com.sfac.springboot.modules.common.service.ExceptionLogService;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.UnauthorizedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;

import javax.servlet.http.HttpServletRequest;

/**
 * ExceptionController
 * 捕获控制器所有异常并进行处理
 *
 */
@ControllerAdvice
public class ExceptionController {

    public final static Logger LOGGER = LoggerFactory.getLogger(ExceptionController.class);

    @Autowired
    private ExceptionLogService exceptionLogService;

    /**
     * 处理控制层所有的异常，根据异常类型和调用类型来决定返回对象
     * 接口调用异常，返回403、404、500对应的json数据
     * 页面调用异常，返回403、404、500对应的错误页面
     */
    @ExceptionHandler(value = Exception.class)
    public ModelAndView handleException(HttpServletRequest request,Exception exception) {

        //输出异常信息
        exception.printStackTrace();
        LOGGER.debug(exception.getMessage());

        //插入异常信息
        insertExceptionLog(request,exception);

        //根据异常类型，封装数据（状态码，消息，异常错误页面对应的路径）
        int status = 200;
        String message = "";
        String data = "";

        //暂将ArithmeticException 作为没有权限异常处理
        if (exception instanceof UnauthorizedException){
            status = 403;
            message = "没有权限";
            data = "/common/403";
        }else {
            status = 500;
            message = "内部错误";
            data = "/common/500";
        }

        //判断请求类型，接口调用则返回json数据，反之返回错误界面
        if (checkCallType(request)){
            ModelAndView mv = new ModelAndView(new MappingJackson2JsonView());
            mv.addObject("status",status);
            mv.addObject("message",message);
            mv.addObject("data",data);
            return mv;
        }else {
            //return new ModelAndView("redirect:/common/404");
            return new ModelAndView(new RedirectView(data));
        }

    }

    //判断请求控制器是接口还是页面
    private boolean checkCallType(HttpServletRequest request){
        HandlerMethod handlerMethod  = (HandlerMethod) request.getAttribute(
                "org.springframework.web.servlet.HandlerMapping.bestMatchingHandler"
        );
        //从方法上面获取ResponseBody 注解
        ResponseBody[] annotations1 = handlerMethod.getMethod().getAnnotationsByType(ResponseBody.class);
        //从类上面获取ResponseBody 注解
        ResponseBody[] annotations2 = handlerMethod.getBeanType().getAnnotationsByType(ResponseBody.class);
        //从类上面获取RestController 注解
        RestController[] annotations3 = handlerMethod.getBeanType().getAnnotationsByType(RestController.class);
        return (annotations1.length > 0) || (annotations2.length > 0) || (annotations3.length > 0);
    }

    //保存异常信息
    private void insertExceptionLog(HttpServletRequest request,
                                    Exception exception){
        ExceptionLog exceptionLog = new ExceptionLog();
        exceptionLog.setIp(request.getRemoteAddr());
        exceptionLog.setPath(request.getServletPath());
        HandlerMethod handlerMethod  = (HandlerMethod) request.getAttribute(
                "org.springframework.web.servlet.HandlerMapping.bestMatchingHandler"
        );
        exceptionLog.setClassName(handlerMethod.getBeanType().getName());
        exceptionLog.setMethodName(handlerMethod.getMethod().getName());
        exceptionLog.setExceptionType(exception.getClass().getSimpleName());
        String message = exception.getMessage();
        int length = 0;
        if (StringUtils.isNotBlank(message)){
            length = message.length() > 200 ? 200 : message.length();
        }
        exceptionLog.setExceptionMessage(StringUtils.isNotBlank(message)
                ? exception.getMessage().substring(0,length) : "");

        exceptionLogService.insertExceptionLog(exceptionLog);
    }
}
